#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include "../include/linux/yapfs.h"

long get_file_size(char *file)
{
    struct stat st;
    if (stat(file, &st) < 0)
        return -1;
    return st.st_size;
}

void erase_flash_bin(int fd, unsigned int size)
{
    char *mapped;
    unsigned long long *tmp;
   
    mapped = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mapped == MAP_FAILED) 
    {
        perror("mmap");
        return;
    }

    tmp = (unsigned long long *)(mapped);
    while ((unsigned long long)tmp < (unsigned long long)(mapped + size))
        *tmp++ = 0xffffffffffffffff;

    msync(mapped, size, MS_SYNC);
   
    munmap(mapped, size);
}

int main(int argc, char *argv[])
{
    int ret;
    int fd_file;
    int fd_image;
    int image_size;
    int file_size;
    char *rwbuf;
    struct yapfs_super_block *sb;
    struct yapfs_inode *inode;
    int name_len;

    if (argc < 3)
    {
        fprintf(stderr, "Usage: %s <file to extract> <image file>\n", argv[0]);
        return -2;
    }

    name_len = strlen(argv[1]);
    if (name_len <= 0 || name_len > (YAPFS_BLOCK_SIZE - sizeof (struct yapfs_super_block) - sizeof(struct yapfs_inode) - 1))
        return -3;

    
    file_size = get_file_size(argv[1]);
    if (file_size < 0)
        return -4;
    
    image_size = get_file_size(argv[2]);
    if (image_size < 0)
        return -5;

    rwbuf = malloc(YAPFS_BLOCK_SIZE);
    if (!rwbuf)
        return -6;
    
    fd_file = open(argv[1], O_RDONLY);    
    if (fd_file < 0)
    {
        ret = -7;
        goto err_open_file;
    }
    
    fd_image = open(argv[2], O_RDWR);
    if (fd_image < 0)
    {
        ret = -8;
        goto err_open_image;
    }

    erase_flash_bin(fd_image, image_size);

    memset(rwbuf, 0xff, YAPFS_BLOCK_SIZE);
    sb = (struct yapfs_super_block *)rwbuf;
    inode = (struct yapfs_inode *)(rwbuf + sizeof(struct yapfs_super_block));

    strcpy(sb->magic, YAPFS_MAGIC);
    sb->block_size = YAPFS_BLOCK_SIZE;
    sb->block_count = image_size / YAPFS_BLOCK_SIZE;
    
    inode->size = file_size;
    inode->start_block = 1;
    strcpy(inode->name, argv[1]);

    ret = write(fd_image, rwbuf, YAPFS_BLOCK_SIZE);
    if (ret != YAPFS_BLOCK_SIZE)
    {
        ret = -9;
        goto err_write;
    }

    while (file_size > 0)
    {
        int to_read = file_size > YAPFS_BLOCK_SIZE ? YAPFS_BLOCK_SIZE : file_size;
        ret = read(fd_file, rwbuf, to_read);
        if (ret != to_read)
        {
            ret = -10;
            goto err_read;
        }

        ret = write(fd_image, rwbuf, to_read);
        if (ret != to_read)
        {
            ret = -11;
            goto err_read;
        }

        file_size -= to_read;
    }

err_read:
err_write:
    close(fd_image);
err_open_image:
    close(fd_file);
err_open_file:
    free(rwbuf);

    return 0;
}